/* //device/system/broncho-ril/reference-ril.c
**
** Copyright 2006, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/

#include <telephony/ril.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <alloca.h>
#include "atchannel.h"
#include "at_tok.h"
#include "ril_util.h"
#include <getopt.h>
#include <sys/socket.h>
#include <termios.h>
#include "broncho-ril.h"
#include "ril-cc.h"
#include "ril-sms.h"
#include "ril-sim.h"
#include "ril-stk.h"
#include "ril-dev.h"
#include "ril-mm.h"
#include "ril-gprs.h"
#include "ril-ss.h"
#include "modemcontrol.h"

#ifndef BRONCHO_TEST
#include <cutils/sockets.h>
#endif

#define LOG_TAG "RIL"
#include "broncho_log.h"

#define MAX_AT_RESPONSE 0x1000


/* pathname returned from RIL_REQUEST_SETUP_DEFAULT_PDP */
#define TTY_DEVICE_PREFIX "/dev/tty"

/* modem audio gain*/
#define MODEM_AUDIO_GAIN_CONFIG_FILE	"/system/etc/modem_gain.conf"

#define MUX_CMD_POWER_ON		"POWER_ON"
#define MUX_CMD_POWER_OFF		"POWER_OFF"
#define MUX_CMD_RESP_OK			"OK"

static void onRequest (int request, void *data, size_t datalen, RIL_Token t);
static RIL_RadioState currentState();
static int onSupports (int requestCode);
static void onCancel (RIL_Token t);
static const char *getVersion();
static void RIL_Destroy(RIL* thiz);
extern const char * requestToString(int request);

static int RIL_InitAtChannels(RIL* thiz);
static int RIL_PowerOff(RIL* thiz);
static void RIL_SetPower(RIL* thiz, void *data, size_t datalen, RIL_Token t);

/*** Static Variables ***/
static const RIL_RadioFunctions s_callbacks = {
    4,
    onRequest,
    currentState,
    onSupports,
    onCancel,
    getVersion
};

enum
{
	CHANNEL_AT,
	CHANNEL_DATA,
	CHANNEL_NR
};

typedef struct _PrivInfo
{
	struct _PrivInfo* next;

	void* info;
	const char* id;
}PrivInfo;

struct _RIL
{
	ATChannel*		channels[CHANNEL_NR];
	int				closed;
	RIL_RadioState 	state;

	int				power_on;

	pthread_mutex_t	state_mutex;
	pthread_cond_t	state_cond;

	const char*		device_path;
	const char* 	socket_name;
	const char* 	mux_name;
	int				loopback_port;

	PrivInfo*		priv_info;
#ifdef RIL_SHLIB
	const struct RIL_Env* rilenv;
#endif
};

static RIL* s_ril = NULL;

/* trigger change to this with thiz->state_cond */
static const struct timeval TIMEVAL_SIMPOLL = {1,0};
static const struct timeval TIMEVAL_CALLSTATEPOLL = {0,500000};
static const struct timeval TIMEVAL_0 = {0,0};
static const struct timeval TIMEVAL_RESET_SLEEP = {10*60, 0};

static void pollSIMState (void *param);

void RIL_SetPrivInfo(RIL* thiz, void* info, const char* id)
{
	PrivInfo*    priv = NULL;

	priv = calloc(1, sizeof(PrivInfo));
	priv->next = NULL;
	priv->id = id;
	priv->info = info;

	if(thiz->priv_info == NULL)
	{
		thiz->priv_info = priv;
	}
	else
	{
		PrivInfo* cur = thiz->priv_info;
		while(cur->next != NULL)
		{
			cur = cur->next;
		}
		cur->next = priv;
	}

	return;
}

void* RIL_GetPrivInfo(RIL* thiz, const char* id)
{
	void* ret = NULL;

	if(thiz->priv_info != NULL)
	{
		PrivInfo* priv = thiz->priv_info;
		do
		{
			if(strcmp(priv->id, id) == 0)
			{
				ret = priv->info;
				break;
			}
			priv = priv->next;
		}while(priv != NULL);
	}

	return ret;
}

void RIL_requestTimedCallback (RIL* thiz, RIL_TimedCallback callback, void *param,
                                const struct timeval *relativeTime)
{
	if (thiz != NULL)
	{
#ifdef RIL_SHLIB
	thiz->rilenv->RequestTimedCallback(callback,param,relativeTime);
#endif
	}
	else
	{
		ALOGE("RIL is NULL\n");
		assert(0);
	}

	return;
}

void RIL_onUnsolicitedResponse(RIL* thiz, int unsolResponse, const void *data, 
                                size_t datalen)
{
	if (thiz != NULL)
	{
#ifdef RIL_SHLIB
	thiz->rilenv->OnUnsolicitedResponse(unsolResponse, data, datalen);
#endif
	}
	else
	{
		ALOGE("RIL is NULL\n");
		assert(0);
	}

	return;
}

void RIL_onRequestComplete(RIL* thiz, RIL_Token t, RIL_Errno e, void *response, size_t responselen)
{
	if (thiz != NULL)
	{
#ifdef RIL_SHLIB
	thiz->rilenv->OnRequestComplete(t, e, response, responselen);
#endif
	}
	else
	{
		ALOGE("RIL is null\n");
		assert(0);
	}

	return;
}

int RIL_GetState (RIL* thiz)
{
	if (thiz != NULL)
	{
		return thiz->state;
	}
	else
	{
		return RADIO_STATE_UNAVAILABLE;
	}
}

static void calAudioParam(RIL* thiz, int type, int level, int gain)
{
	ATResponse* p_resp =  NULL;
	char cmd[128] = {0};

	snprintf(cmd, 128, "AT+EADP=0,0,%d,%d", type, level);
	if(ril_command_exec_singleline_for_result(thiz, cmd, "+EADP:", &p_resp))
	{
		int realGain = 0;

		at_line_scanf_int(p_resp->p_intermediates->line, &realGain);
		if(realGain != gain)
		{
			snprintf(cmd, 128, "AT+EADP=1,0,%d,%d,%d", type, level, gain);
			ril_command_exec_for_result(thiz, cmd);
		}
	}
	at_response_free(p_resp);

	return;
}

static int broncho_getline(char** line, int* len, FILE* fp)
{
	static char* buffer = NULL;
	static int buf_len = 0;
	static int read_pos = 0;
	int line_len = 0;
	int i = 0;

	if(*line == NULL)
	{
		if(buffer != NULL)
		{
			free(buffer);
			buffer = NULL;
		}
		buf_len = 0;
		read_pos = 0;
	}
	
	if(buffer == NULL)
	{
		fseek(fp, 0, SEEK_END);
		buf_len = ftell(fp);
		fseek(fp, 0, SEEK_SET);
		buffer = calloc(1, buf_len);
		fread(buffer, buf_len, 1, fp);
	}

	while(buffer[read_pos] == '\r' || buffer[read_pos] == '\n')
	{
		read_pos++;
	}

	for(i = read_pos; i < buf_len; i++)
	{
		if(buffer[i] == '\r' || buffer[i] == '\n')
		{
			break;
		}
	}
	

	line_len = i - read_pos;
	if(*len < line_len)
	{
		*line = realloc(*line, line_len);
		*len = line_len;
	}
	memcpy(*line, buffer + read_pos, line_len);
	read_pos += line_len;
	

	return line_len;
}

static void checkAudioParam(RIL* thiz)
{
	FILE* fp = fopen(MODEM_AUDIO_GAIN_CONFIG_FILE, "r");
	char * line = NULL;
	int len = 0;
	int read = 0;

	if(fp != NULL)
	{
		while ((read = broncho_getline(&line, &len, fp)) > 0) 
		{
			int level = 0;
			int gain = 0;
			int type = 0;
			
			ALOGD("checkAudioParam: line=%s\n", line);
			if(strStartsWith(line, "speech:"))
			{
				type = 2;
			}
			else if(strStartsWith(line, "mic:"))
			{
				type = 3;
			}
			else if(strStartsWith(line, "sidetone:"))
			{
				type = 4;
			}
			
			if(at_line_scanf_int_int(line, &level, &gain))
			{
				calAudioParam(thiz, type, level, gain);
			}
			ALOGD("checkAudioParam: type=%d, level=%d, gain=%d\n", type, level, gain);
		}

		if (line != NULL)
		{
			free(line);
		}
		fclose(fp);
	}

	return;
}

/** do post- SIM ready initialization */
static void onSIMReady(RIL* thiz)
{
	ATResponse *p_resp = NULL;
    //ril_send_command_singleline(thiz, "AT+CSMS=1", "+CSMS:", NULL);
    /*
     * Always send SMS messages directly to the TE
     *
     * mode = 1 // discard when link is reserved (link should never be
     *             reserved)
     * mt = 2   // most messages routed to TE
     * bm = 2   // new cell BM's routed to TE
     * ds = 1   // Status reports routed to TE
     * bfr = 1  // flush buffer
     */
    ril_send_command(thiz, "AT+CNMI=1,2,2,1,1", NULL);
	
	/* Broncho delete begin by yinlijun.
	 * This command will waste lots of time, so we ignore it.
	 * But make sure that we keep AT+CNMI=1,2,2,1,1
	 * This is VERY IMPORTANT!
	 * because only in that case,  the sms will be pushed to the TE derectly.
	 * */
	/*
	if(ril_command_exec_singleline_for_result(thiz, "AT+CPMS=\"SM\",\"SM\",\"SM\"", "+CPMS", &p_resp))
	{
		int total = 0;
		int used = 0;

		at_line_scanf_int_int(p_resp->p_intermediates->line, &used, &total);
		if(total > 0 && total == used)
		{
			RIL_onUnsolicitedResponse(thiz, RIL_UNSOL_SIM_SMS_STORAGE_FULL, NULL, 0);
		}
	}
	at_response_free(p_resp);
	*/
	/*Broncho delete end by yinlijun*/
	
	checkAudioParam(thiz);

	return;
}

/*** Callback methods from the RIL library to us ***/
/**
 * Call from RIL to us to make a RIL_REQUEST
 *
 * Must be completed with a call to RIL_onRequestComplete()
 *
 * RIL_onRequestComplete() may be called from any thread, before or after
 * this function returns.
 *
 * Will always be called from the same thread, so returning here implies
 * that the radio is ready to process another command (whether or not
 * the previous command has completed).
 */
static void
onRequest (int request, void *data, size_t datalen, RIL_Token t)
{
	RIL* thiz = s_ril;

	ALOGI ("%s: request=%d, state=%d\n", __func__, request, thiz->state);
	
	if(request == RIL_REQUEST_POWER)
	{
		RIL_SetPower(thiz, data, datalen, t);
		return;
	}

    /* Ignore all requests except RIL_REQUEST_GET_SIM_STATUS
     * when RADIO_STATE_UNAVAILABLE.
     */
    if (thiz->state == RADIO_STATE_UNAVAILABLE
        && request != RIL_REQUEST_GET_SIM_STATUS
    ) {
        RIL_onRequestComplete(thiz, t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0);
        return;
    }

    /* Ignore all non-power requests when RADIO_STATE_OFF
     * (except RIL_REQUEST_GET_SIM_STATUS)
     */
    if (thiz->state == RADIO_STATE_OFF
        && !(request == RIL_REQUEST_RADIO_POWER
            || request == RIL_REQUEST_GET_SIM_STATUS)
    ) {
        RIL_onRequestComplete(thiz, t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0);
        return;
    }

    switch (request) 
	{
	case RIL_REQUEST_GET_SIM_STATUS:
	case RIL_REQUEST_SIM_IO:        
	case RIL_REQUEST_ENTER_SIM_PIN:
	case RIL_REQUEST_ENTER_SIM_PUK:
	case RIL_REQUEST_ENTER_SIM_PIN2:
	case RIL_REQUEST_ENTER_SIM_PUK2:
	case RIL_REQUEST_GET_SIM_PIN_REMAIN_TIMES:
	case RIL_REQUEST_CHANGE_SIM_PIN:
	case RIL_REQUEST_CHANGE_SIM_PIN2:
	case RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION:
	case RIL_REQUEST_GET_IMSI:
		RIL_onRequest_sim (thiz, request, data, datalen, t);
		break;

	case RIL_REQUEST_STK_GET_PROFILE:
	case RIL_REQUEST_STK_SET_PROFILE:
	case RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND:
	case RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE:
	case RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM:
	case RIL_REQUEST_STK_SEND_SMS:
	case RIL_REQUEST_STK_SEND_DTMF:
	case RIL_REQUEST_STK_SEND_USSD:
	case RIL_REQUEST_STK_SEND_SS:
	case RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING:
		RIL_onRequest_stk (thiz, request, data, datalen, t);
		break;


	case RIL_REQUEST_SIGNAL_STRENGTH:
	case RIL_REQUEST_VOICE_REGISTRATION_STATE:
	case RIL_REQUEST_OPERATOR:
	case RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE:
	case RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC:
	case RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL:
	case RIL_REQUEST_QUERY_AVAILABLE_NETWORKS:
	case RIL_REQUEST_SCREEN_STATE:
	case RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE:
	case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE:
	case RIL_REQUEST_GET_NEIGHBORING_CELL_IDS:
	case RIL_REQUEST_SET_LOCATION_UPDATES:
		RIL_onRequest_mm (thiz, request, data, datalen, t);
		break;

	case RIL_REQUEST_GET_CURRENT_CALLS:
	case RIL_REQUEST_DIAL:
	case RIL_REQUEST_HANGUP:
	case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND:
	case RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND:
	case RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE:
	case RIL_REQUEST_CONFERENCE:
	case RIL_REQUEST_UDUB:
	case RIL_REQUEST_LAST_CALL_FAIL_CAUSE:
	case RIL_REQUEST_DTMF:
	case RIL_REQUEST_ANSWER:
	case RIL_REQUEST_DTMF_START:
	case RIL_REQUEST_DTMF_STOP:
	case RIL_REQUEST_SEPARATE_CONNECTION:
	case RIL_REQUEST_EXPLICIT_CALL_TRANSFER:
		RIL_onRequest_cc (thiz, request, data, datalen, t);
		break;

	case RIL_REQUEST_RADIO_POWER:
	case RIL_REQUEST_GET_IMEI:
	case RIL_REQUEST_GET_IMEISV:
	case RIL_REQUEST_BASEBAND_VERSION:
	case RIL_REQUEST_SET_MUTE:
	case RIL_REQUEST_GET_MUTE:
	case RIL_REQUEST_RESET_RADIO:
	case RIL_REQUEST_OEM_HOOK_RAW:
	case RIL_REQUEST_OEM_HOOK_STRINGS:
	case RIL_REQUEST_SET_BAND_MODE:
	case RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE:
		RIL_onRequest_dev (thiz, request, data, datalen, t);
		break;

	case RIL_REQUEST_SEND_SMS:
	case RIL_REQUEST_SEND_SMS_EXPECT_MORE:
	case RIL_REQUEST_SMS_ACKNOWLEDGE:
	case RIL_REQUEST_WRITE_SMS_TO_SIM:
	case RIL_REQUEST_DELETE_SMS_ON_SIM:
	case RIL_REQUEST_GET_SMSC_ADDRESS:
	case RIL_REQUEST_SET_SMSC_ADDRESS:
	case RIL_REQUEST_REPORT_SMS_MEMORY_STATUS:
	case RIL_REQUEST_GSM_GET_BROADCAST_SMS_CONFIG:
	case RIL_REQUEST_GSM_SET_BROADCAST_SMS_CONFIG:
	case RIL_REQUEST_GSM_SMS_BROADCAST_ACTIVATION:
		RIL_onRequest_sms (thiz, request, data, datalen, t);
		break;

	case RIL_REQUEST_SETUP_DATA_CALL:
	case RIL_REQUEST_DEACTIVATE_DATA_CALL:
	case RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE:
	case RIL_REQUEST_DATA_CALL_LIST:
	case RIL_REQUEST_DATA_REGISTRATION_STATE:
		RIL_onRequest_gprs (thiz, request, data, datalen, t);
		break;

	case RIL_REQUEST_SEND_USSD:
	case RIL_REQUEST_CANCEL_USSD:
	case RIL_REQUEST_GET_CLIR:
	case RIL_REQUEST_SET_CLIR:
	case RIL_REQUEST_QUERY_CALL_FORWARD_STATUS:
	case RIL_REQUEST_SET_CALL_FORWARD:
	case RIL_REQUEST_QUERY_CALL_WAITING:
	case RIL_REQUEST_SET_CALL_WAITING:
	case RIL_REQUEST_QUERY_FACILITY_LOCK:
	case RIL_REQUEST_SET_FACILITY_LOCK:
	case RIL_REQUEST_CHANGE_BARRING_PASSWORD:
	case RIL_REQUEST_QUERY_CLIP:
	case RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION:
		RIL_onRequest_ss (thiz, request, data, datalen, t);
		break;

	default:
		RIL_onRequestComplete(thiz, t, RIL_E_REQUEST_NOT_SUPPORTED, NULL, 0);
		break;
    }
}

/**
 * Synchronous call from the RIL to us to return current radio state.
 * RADIO_STATE_UNAVAILABLE should be the initial state.
 */
static RIL_RadioState
currentState()
{
	RIL* thiz = s_ril;

    return thiz->state;
}
/**
 * Call from RIL to us to find out whether a specific request code
 * is supported by this implementation.
 *
 * Return 1 for "supported" and 0 for "unsupported"
 */

static int
onSupports (int requestCode)
{
	RIL* thiz = s_ril;
    //@@@ todo

	thiz = thiz;
    return 1;
}

static void onCancel (RIL_Token t)
{
	RIL* thiz = s_ril;
    //@@@todo
	thiz = thiz;	
	return;
}

static const char * getVersion(void)
{
	RIL* thiz = s_ril;

	(void)thiz;
    return "android broncho-ril 1.0";
}

void RIL_SetRadioState (RIL* thiz, RIL_RadioState newState)
{
    RIL_RadioState oldState = 0;

    pthread_mutex_lock(&thiz->state_mutex);
    oldState = thiz->state ;

	ALOGI("%s: newState=%d\n", __func__, newState);
    if (thiz->closed > 0) {
        // If we're closed, the only reasonable state is
        // RADIO_STATE_UNAVAILABLE
        // This is here because things on the main thread
        // may attempt to change the radio state after the closed
        // event happened in another thread
        newState = RADIO_STATE_UNAVAILABLE;
    }

    if (thiz->state != newState || thiz->closed > 0) {
        thiz->state = newState;

        pthread_cond_broadcast (&thiz->state_cond);
    }
    pthread_mutex_unlock(&thiz->state_mutex);


    /* do these outside of the mutex */
    if (thiz->state != oldState) {
        RIL_onUnsolicitedResponse (thiz, RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED,
                                    NULL, 0);
        /* FIXME onSimReady() and onRadioPowerOn() cannot be called
         * from the AT reader thread
         * Currently, this doesn't happen, but if that changes then these
         * will need to be dispatched on the request thread
         */
        if (thiz->state == RADIO_STATE_SIM_READY) {
            onSIMReady(thiz);
        } else if (thiz->state == RADIO_STATE_SIM_NOT_READY) {
            pollSIMState(thiz);
        }
    }
}

/**
 * SIM ready means any commands that access the SIM will work, including:
 *  AT+CPIN, AT+CSMS, AT+CNMI, AT+CRSM
 *  (all SMS-related commands)
 */

static void pollSIMState (void *param)
{
	RIL* thiz = (RIL*)param;
	
	/*
    if (thiz->state != RADIO_STATE_SIM_NOT_READY) {
        // no longer valid to poll
        return;
    }
	*/

    switch(RIL_getSIMStatus(thiz)) {
        case RIL_SIM_ABSENT:
        case RIL_SIM_PIN:
        case RIL_SIM_PUK:
        case RIL_SIM_NETWORK_PERSONALIZATION:
        default:
            RIL_SetRadioState (thiz, RADIO_STATE_SIM_LOCKED_OR_ABSENT);
        break;

        case RIL_SIM_NOT_READY:
            RIL_requestTimedCallback (thiz, pollSIMState, thiz, &TIMEVAL_SIMPOLL);
        break;

        case RIL_SIM_READY:
            RIL_SetRadioState (thiz, RADIO_STATE_SIM_READY);
        break;
    }

	return;
}

void RIL_pollSIMState(RIL* thiz)
{
	RIL_requestTimedCallback (thiz, pollSIMState, thiz, &TIMEVAL_SIMPOLL);
}

/**
 * Initialize everything that can be configured while we're still in
 * AT+CFUN=0
 */
static void initializeCallback(void *param)
{
	RIL* thiz = (RIL*)param;

	
    RIL_SetRadioState (thiz, RADIO_STATE_UNAVAILABLE);
    at_handshake(RIL_GetAtChannel(thiz));

    /* note: we don't check errors here. Everything important will
       be handled in onATTimeout and onATReaderClosed */

    /*  atchannel is tolerant of echo but it must */
    /*  have verbose result codes */
    ril_send_command(thiz, "ATE0", NULL);
	ril_send_command(thiz, "AT+CMEE=1", NULL);
	ril_send_command(thiz, "ATS0=0", NULL);
	ril_send_command(thiz, "ATH", NULL);
	ril_command_exec_full_for_result(thiz, "AT+CFUN=4", 20000);

	RIL_initialize_dev(thiz);
	RIL_initialize_sim(thiz);
	RIL_initialize_cc(thiz);
	RIL_initialize_sms(thiz);
	RIL_initialize_mm(thiz);
	RIL_initialize_ss(thiz);
	RIL_initialize_stk(thiz);
	RIL_initialize_gprs(thiz);
	
	RIL_SetRadioState (thiz, RADIO_STATE_OFF);

	return;
}

static void waitForClose(RIL* thiz)
{
    pthread_mutex_lock(&thiz->state_mutex);

	ALOGD("waitForClose\n");
    while (thiz->closed == 0) {
		ALOGD("waitForClose: pthread_cond_wait\n");
        pthread_cond_wait(&thiz->state_cond, &thiz->state_mutex);
    }

    pthread_mutex_unlock(&thiz->state_mutex);
}

/**
 * Called by atchannel when an unsolicited line appears
 * This is called on atchannel's reader thread. AT commands may
 * not be issued here
 */
void onUnsolicited (ATChannel* channel, const char *s, const char *sms_pdu, void* user_data)
{
	int match = 0;
	RIL* thiz = (RIL*)user_data;

	/*TODO: MTK stk url*/
	if ((match = RIL_onUnsolicited_stk(thiz, s)))
	{
		ALOGI("onUnsolicited: stk handled\n");
		return;
	}

	if (thiz->state == RADIO_STATE_UNAVAILABLE) 
	{
        return;
    }

	do
	{
		if ((match = RIL_onUnsolicited_cc(thiz, s)))
		{
			ALOGI("onUnsolicited: cc handled\n");
			break;
		}

		if ((match = RIL_onUnsolicited_sms(thiz, s, sms_pdu)))
		{
			ALOGI("onUnsolicited: sms handled\n");
			break;
		}

		if ((match = RIL_onUnsolicited_mm (thiz, s)))
		{
			ALOGI("onUnsolicited: mm handled\n");
			break;
		}
		
		if ((match = RIL_onUnsolicited_dev(thiz, s)))
		{
			ALOGI("onUnsolicited: dev handled\n");
			break;
		}

		if ((match = RIL_onUnsolicited_ss(thiz, s)))
		{
			ALOGI("onUnsolicited: ss handled\n");
			break;
		}

		if ((match = RIL_onUnsolicited_gprs(thiz, s)))
		{
			ALOGI("onUnsolicited: gprs handled");
			break;
		}

	}while (0);

	return;
}

/* Called on command or reader thread */
static void onATReaderClosed(ATChannel* channel, void* user_data)
{
	int i = 0;
	RIL* thiz = (RIL*)user_data;

    ALOGI("AT channel closed\n");
	for (i = 0; i < CHANNEL_NR; i++)
	{
		if (channel == thiz->channels[i])
		{
			//thiz->channels[i] = NULL;
			break;
		}
	}

	if (i == CHANNEL_AT)
	{
    	thiz->closed = 1;
	    RIL_SetRadioState (thiz, RADIO_STATE_UNAVAILABLE);
	}

	return;
}

/* Called on command thread */
static void onATTimeout(ATChannel* channel, void* user_data)
{
	RIL* thiz = (RIL*)user_data;

    ALOGI("AT channel timeout; closing\n");
	if(channel == thiz->channels[CHANNEL_AT])
	{
    	at_channel_close(channel);
	    thiz->closed = 1;
    	RIL_SetRadioState (thiz, RADIO_STATE_UNAVAILABLE);
	}

	return;
}

static void usage(char *s)
{
#ifdef RIL_SHLIB
    fprintf(stderr, "reference-ril requires: -p <tcp port> or -d /dev/tty_device\n");
#else
    fprintf(stderr, "usage: %s [-p <tcp port>] [-d /dev/tty_device]\n", s);
    exit(-1);
#endif
}

static int RIL_OpenAtChannel(RIL* thiz, RilPortType type, 
				const char* name, int channel_no)
{
	int ret = 0;

	if(thiz->channels[channel_no] == NULL)
	{
		thiz->channels[channel_no] = at_channel_create (type, 
									name, onUnsolicited, thiz);
		at_channel_set_on_timeout(thiz->channels[channel_no], 
				onATTimeout, thiz);
		at_channel_set_on_closed(thiz->channels[channel_no], 
				onATReaderClosed, thiz);
	}
	ret = at_channel_open(thiz->channels[channel_no]);

	return ret;
}

static int RIL_CloseMux(const char* channel_name)
{
	int ret = 0;
	struct timeval tv ;
	int fd = -1;

	if((fd = ril_open_port(PORT_TYPE_PTY, channel_name)) > 0)
	{
		fd_set fds;
		char	resp[64] ={0};
		int		resp_len = 0;

		write(fd, MUX_CMD_POWER_OFF, strlen(MUX_CMD_POWER_OFF));
		FD_ZERO(&fds);
		FD_SET(fd, &fds);
		gettimeofday(&tv, (struct timezone *) NULL);
		tv.tv_sec = 30;
		tv.tv_usec = 0;
		select(fd + 1, &fds, NULL, NULL, &tv);
		if(FD_ISSET(fd, &fds))
		{
			if ((resp_len = read(fd, resp, 64)) > 0 && strncmp(resp, MUX_CMD_RESP_OK, strlen(MUX_CMD_RESP_OK)) == 0)
			{
				ret = 1;
			}
		}
		close(fd);
	}
	
	return ret;
}

static int RIL_StartMux(const char* channel_name)
{
	int ret = 0;
	struct timeval tv ;
	int fd = -1;

	if((fd = ril_open_port(PORT_TYPE_PTY, channel_name)) > 0)
	{
		fd_set fds;
		char	resp[64] ={0};
		int		resp_len = 0;

		write(fd, MUX_CMD_POWER_ON, strlen(MUX_CMD_POWER_ON));
		FD_ZERO(&fds);
		FD_SET(fd, &fds);
		gettimeofday(&tv, (struct timezone *) NULL);
		tv.tv_sec = 30;
		tv.tv_usec = 0;
		select(fd + 1, &fds, NULL, NULL, &tv);
		if(FD_ISSET(fd, &fds))
		{
			if ((resp_len = read(fd, resp, 64)) > 0 && strncmp(resp, MUX_CMD_RESP_OK, strlen(MUX_CMD_RESP_OK)) == 0)
			{
				ret = 1;
			}
		}
		close(fd);
	}

	return ret;
}

static int RIL_OpenMuxChannels(RIL* thiz)
{
	int 	ret = 0;
	char	channel_name[255 + 1] = {0};

	do
	{
		snprintf(channel_name, 255, "%s2", thiz->mux_name);	
		if ((ret = RIL_StartMux(channel_name)) == 0)
		{
			property_set("ctl.stop", "gsmmux-daemon");
			sleep(2);
			property_set("ctl.start", "gsmmux-daemon");

			ALOGI("can't  start mux\n");
			break;
		}

		snprintf(channel_name, 255, "%s0", thiz->mux_name);
		RIL_OpenAtChannel (thiz, PORT_TYPE_PTY, channel_name, CHANNEL_AT);

		snprintf(channel_name, 255, "%s1", thiz->mux_name);
		RIL_OpenAtChannel (thiz, PORT_TYPE_PTY, channel_name, CHANNEL_DATA);
	}while(0);
	ALOGI("RIL_OpenMuxChannels %d\n", ret);

	return ret;
}

void RIL_OpenDataChannel(RIL* thiz)
{
	if(thiz->channels[CHANNEL_DATA] != NULL)
	{
		at_channel_open(thiz->channels[CHANNEL_DATA]);
	}

	return;
}

void RIL_CloseDataChannel(RIL* thiz)
{
	if(thiz->channels[CHANNEL_DATA] != NULL)
	{
		at_channel_close(thiz->channels[CHANNEL_DATA]);
	}

	return;
}

static int RIL_PowerOff(RIL* thiz)
{
	int ret = 0;

	ril_command_exec_full_for_result(thiz, "ATH", 10000);
	ril_command_exec_full_for_result(thiz, "AT+CFUN=4", 20000);
	if (ril_command_exec_full_for_result(thiz, "AT+EPOF", 20000))
	{
		ret = 1;
	}

	if (thiz->mux_name != NULL)
	{
		char	channel_name[255 + 1] = {0};

		snprintf(channel_name, 255, "%s2", thiz->mux_name);	
		RIL_CloseMux(channel_name);
	}
	
	return ret;
}

static void RIL_SetPower(RIL* thiz, void *data, size_t datalen, RIL_Token t)
{
	int onOff = 0;
	int result = 0;

	onOff = ((int *)data)[0];
	if(onOff)
	{
		result = RIL_InitAtChannels(thiz);
	}
	else
	{
		result = RIL_PowerOff(thiz);
	}
	
	if(result)
	{
		RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, NULL, 0);
	}
	else
	{
		RIL_onRequestComplete(thiz, t, RIL_E_GENERIC_FAILURE, NULL, 0);
	}

	return;
}

static int RIL_InitAtChannels(RIL* thiz)
{
	int opend = 0;

	if(thiz->channels[CHANNEL_AT] != NULL)
	{
		at_channel_destroy(thiz->channels[CHANNEL_AT]);
		thiz->channels[CHANNEL_AT] = NULL;
	}

	while(!opend)
	{
		if (thiz->loopback_port > 0) 
		{
			opend = RIL_OpenAtChannel(thiz, PORT_TYPE_LOOPBACK, 
					(const char*)thiz->loopback_port, CHANNEL_AT);
		} 
		else if (thiz->socket_name != NULL) 
		{
			opend = RIL_OpenAtChannel(thiz, PORT_TYPE_SOCKET,
					thiz->socket_name, CHANNEL_AT);
		} 
		else if (thiz->mux_name != NULL) 
		{
			opend = RIL_OpenMuxChannels(thiz);
		}
		else if(thiz->device_path != NULL)
		{

			int fd = 0;

			fd = open("/dev/modem", O_RDWR);

			ioctl(fd, MODEM_PWR_ON);
			close (fd);
			sleep(10);
			opend = RIL_OpenAtChannel(thiz, PORT_TYPE_SERIAL,
						thiz->device_path, CHANNEL_AT);
        }

		if (!opend)
		{
			sleep(10);
		}
	}
	thiz->closed = 0;

	return 1;
}

static void *
mainLoop(void *param)
{
    int ret = 0;
	RIL* thiz = (RIL*)param;

	ALOGD ("broncho RILD: mainloop\n");
    AT_DUMP("== ", "entering mainLoop()", -1 );
	
	thiz->power_on = 1;
    for (;;) {
		sleep(2);

		if (!RIL_InitAtChannels(thiz))
		{
            ALOGE ("AT error %d on at_open\n", ret);
            return NULL;
        }
        RIL_requestTimedCallback(thiz, initializeCallback, thiz, &TIMEVAL_0);
#ifdef BRONCHO_TEST
		initializeCallback(thiz);
#endif
        // Give initializeCallback a chance to dispatched, since
        // we don't presently have a cancellation mechanism
        sleep(1);
		
        waitForClose(thiz);

		/*don't retry when power off*/
		while(thiz->power_on == 0)
		{
			sleep(1000);
		}
        ALOGI("Re-opening after close");
    }
	RIL_Destroy(thiz);

	return NULL;
}

static void RIL_InitArgvs(RIL* thiz, int argc, char **argv)
{
	int opt = 0;

	while ( -1 != (opt = getopt(argc, argv, "p:d:s:m:"))) {
        switch (opt) {
            case 'p':
                thiz->loopback_port = atoi(optarg);
                ALOGI("Opening loopback port %d\n", thiz->loopback_port);
            break;

            case 'd':
                thiz->device_path = optarg;
                ALOGI("Opening tty device %s\n", thiz->device_path);
            break;

            case 's':
                thiz->socket_name = optarg;
                ALOGI("Opening socket %s\n", thiz->socket_name);
            break;

			case 'm':
				thiz->mux_name = optarg;
				ALOGI("Opening mux %s\n", thiz->mux_name);
			break;

            default:
                usage(argv[0]);
                return;
        }
    }

	if (thiz->loopback_port <= 0 
		&& thiz->device_path == NULL 
		&& thiz->socket_name == NULL
		&& thiz->mux_name == NULL)
	{
		thiz->mux_name = "/dev/mtk";
	}

	return;
}

ATChannel* RIL_GetAtChannel(RIL* thiz)
{
	ATChannel* channel = NULL;

	if (thiz != NULL)
	{
		channel = thiz->channels[CHANNEL_AT];
	}

	return channel;
}

ATChannel* RIL_GetDataChannel(RIL* thiz)
{
	ATChannel* channel = NULL;

	if (thiz != NULL)
	{
		channel = thiz->channels[CHANNEL_DATA];
	}

	return channel;
}

static RIL* RIL_Create ()
{
	RIL* thiz = calloc(1, sizeof (RIL));
	pthread_mutex_init(&thiz->state_mutex, NULL);
	pthread_cond_init(&thiz->state_cond, NULL);

	return thiz;
}

static void RIL_Destroy(RIL* thiz)
{
	int i = 0;

	for(i = 0; i < CHANNEL_NR; i++)
	{
		if(thiz->channels[i] != NULL)
		{
			at_channel_destroy(thiz->channels[i]);
			thiz->channels[i] = NULL;
		}
	}

	free(thiz);

	return;
}

#ifdef RIL_SHLIB
pthread_t s_tid_mainloop;
const RIL_RadioFunctions *RIL_Init(const struct RIL_Env *env, int argc, char **argv)
{
    int ret = 0;
    pthread_attr_t attr;
	RIL* thiz = NULL;

	s_ril = RIL_Create();
	thiz = s_ril;
    thiz->rilenv = env;
	RIL_InitArgvs(thiz, argc, argv);
    if (thiz->loopback_port <= 0 
		&& thiz->socket_name == NULL 
		&& thiz->device_path == NULL
		&& thiz->mux_name == NULL) 
	{
        usage(argv[0]);
        return NULL;
    }
	
	if(thiz->mux_name != NULL) {
		ALOGD("broncho_rild: mux_name=%s\n", thiz->mux_name);
	} else {
		ALOGD("broncho_rild: mux_name = NULL\n");
	}
    pthread_attr_init (&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    ret = pthread_create(&s_tid_mainloop, &attr, mainLoop, thiz);
	printf ("broncho: RIL_Init\n");

    return &s_callbacks;
}
#else /* RIL_SHLIB */
int main (int argc, char **argv)
{
    int ret;
    int fd = -1;
    int opt = 0;
	RIL* thiz = NULL;
	
	s_ril = RIL_Create(ril);
	thiz->s_ril;
	RIL_InitArgvs(thiz, argc, argv);
    if (thiz->loopback_port <= 0 
		&& thiz->socket_name == NULL
		&& thiz->device_path == NULL
		&& thiz->mux_name == NULL) 
	{
        usage(argv[0]);
    }

    //RIL_register(&s_callbacks);
    mainLoop(thiz);

    return 0;
}

#endif /* RIL_SHLIB */

